#pragma once

#include "../widgets.h"
#include "basechart_layer.h"
#include "basechart_style.h"

class BaseChartAxis;
class BaseChartCustomPlot;
class BaseChartAbstractPlottable;
class S_WIDGETS_EXPORT BaseChartRange {
 public:
  double lower, upper;

  BaseChartRange();
  BaseChartRange(double lower, double upper);

  bool operator==(const BaseChartRange& other) const {
    return lower == other.lower && upper == other.upper;
  }
  bool operator!=(const BaseChartRange& other) const {
    return !(*this == other);
  }

  BaseChartRange& operator+=(const double& value) {
    lower += value;
    upper += value;
    return *this;
  }
  BaseChartRange& operator-=(const double& value) {
    lower -= value;
    upper -= value;
    return *this;
  }
  BaseChartRange& operator*=(const double& value) {
    lower *= value;
    upper *= value;
    return *this;
  }
  BaseChartRange& operator/=(const double& value) {
    lower /= value;
    upper /= value;
    return *this;
  }
  friend inline const BaseChartRange operator+(const BaseChartRange&, double);
  friend inline const BaseChartRange operator+(double, const BaseChartRange&);
  friend inline const BaseChartRange operator-(const BaseChartRange& range,
                                               double value);
  friend inline const BaseChartRange operator*(const BaseChartRange& range,
                                               double value);
  friend inline const BaseChartRange operator*(double value,
                                               const BaseChartRange& range);
  friend inline const BaseChartRange operator/(const BaseChartRange& range,
                                               double value);

  double size() const { return upper - lower; }
  double center() const { return (upper + lower) * 0.5; }
  void normalize() {
    if (lower > upper) qSwap(lower, upper);
  }
  void expand(const BaseChartRange& otherRange);
  void expand(double includeCoord);
  BaseChartRange expanded(const BaseChartRange& otherRange) const;
  BaseChartRange expanded(double includeCoord) const;
  BaseChartRange bounded(double lowerBound, double upperBound) const;
  BaseChartRange sanitizedForLogScale() const;
  BaseChartRange sanitizedForLinScale() const;
  bool contains(double value) const { return value >= lower && value <= upper; }

  static bool validRange(double lower, double upper);
  static bool validRange(const BaseChartRange& range);
  static const double minRange;
  static const double maxRange;
};

/*! \relates BaseChartRange

  Prints \a range in a human readable format to the qDebug output.
*/
inline QDebug operator<<(QDebug d, const BaseChartRange& range) {
  d.nospace() << "BaseChartRange(" << range.lower << ", " << range.upper << ")";
  return d.space();
}

/*!
  Adds \a value to both boundaries of the range.
*/
inline const BaseChartRange operator+(const BaseChartRange& range,
                                      double value) {
  BaseChartRange result(range);
  result += value;
  return result;
}

/*!
  Adds \a value to both boundaries of the range.
*/
inline const BaseChartRange operator+(double value,
                                      const BaseChartRange& range) {
  BaseChartRange result(range);
  result += value;
  return result;
}

/*!
  Subtracts \a value from both boundaries of the range.
*/
inline const BaseChartRange operator-(const BaseChartRange& range,
                                      double value) {
  BaseChartRange result(range);
  result -= value;
  return result;
}

/*!
  Multiplies both boundaries of the range by \a value.
*/
inline const BaseChartRange operator*(const BaseChartRange& range,
                                      double value) {
  BaseChartRange result(range);
  result *= value;
  return result;
}

/*!
  Multiplies both boundaries of the range by \a value.
*/
inline const BaseChartRange operator*(double value,
                                      const BaseChartRange& range) {
  BaseChartRange result(range);
  result *= value;
  return result;
}

/*!
  Divides both boundaries of the range by \a value.
*/
inline const BaseChartRange operator/(const BaseChartRange& range,
                                      double value) {
  BaseChartRange result(range);
  result /= value;
  return result;
}

/* end of 'src/axis/range.h' */

/* including file 'src/selection.h'        */
/* modified 2021-03-29T02:30:44, size 8569 */

class S_WIDGETS_EXPORT BaseChartDataRange {
 public:
  BaseChartDataRange();
  BaseChartDataRange(int begin, int end);

  bool operator==(const BaseChartDataRange& other) const {
    return mBegin == other.mBegin && mEnd == other.mEnd;
  }
  bool operator!=(const BaseChartDataRange& other) const {
    return !(*this == other);
  }

  // getters:
  int begin() const { return mBegin; }
  int end() const { return mEnd; }
  int size() const { return mEnd - mBegin; }
  int length() const { return size(); }

  // setters:
  void setBegin(int begin) { mBegin = begin; }
  void setEnd(int end) { mEnd = end; }

  // non-property methods:
  bool isValid() const { return (mEnd >= mBegin) && (mBegin >= 0); }
  bool isEmpty() const { return length() == 0; }
  BaseChartDataRange bounded(const BaseChartDataRange& other) const;
  BaseChartDataRange expanded(const BaseChartDataRange& other) const;
  BaseChartDataRange intersection(const BaseChartDataRange& other) const;
  BaseChartDataRange adjusted(int changeBegin, int changeEnd) const {
    return BaseChartDataRange(mBegin + changeBegin, mEnd + changeEnd);
  }
  bool intersects(const BaseChartDataRange& other) const;
  bool contains(const BaseChartDataRange& other) const;

 private:
  // property members:
  int mBegin, mEnd;
};

class S_WIDGETS_EXPORT BaseChartDataSelection {
 public:
  explicit BaseChartDataSelection();
  explicit BaseChartDataSelection(const BaseChartDataRange& range);

  bool operator==(const BaseChartDataSelection& other) const;
  bool operator!=(const BaseChartDataSelection& other) const {
    return !(*this == other);
  }
  BaseChartDataSelection& operator+=(const BaseChartDataSelection& other);
  BaseChartDataSelection& operator+=(const BaseChartDataRange& other);
  BaseChartDataSelection& operator-=(const BaseChartDataSelection& other);
  BaseChartDataSelection& operator-=(const BaseChartDataRange& other);
  friend inline const BaseChartDataSelection operator+(
      const BaseChartDataSelection& a, const BaseChartDataSelection& b);
  friend inline const BaseChartDataSelection operator+(
      const BaseChartDataRange& a, const BaseChartDataSelection& b);
  friend inline const BaseChartDataSelection operator+(
      const BaseChartDataSelection& a, const BaseChartDataRange& b);
  friend inline const BaseChartDataSelection operator+(
      const BaseChartDataRange& a, const BaseChartDataRange& b);
  friend inline const BaseChartDataSelection operator-(
      const BaseChartDataSelection& a, const BaseChartDataSelection& b);
  friend inline const BaseChartDataSelection operator-(
      const BaseChartDataRange& a, const BaseChartDataSelection& b);
  friend inline const BaseChartDataSelection operator-(
      const BaseChartDataSelection& a, const BaseChartDataRange& b);
  friend inline const BaseChartDataSelection operator-(
      const BaseChartDataRange& a, const BaseChartDataRange& b);

  // getters:
  int dataRangeCount() const { return mDataRanges.size(); }
  int dataPointCount() const;
  BaseChartDataRange dataRange(int index = 0) const;
  QList<BaseChartDataRange> dataRanges() const { return mDataRanges; }
  BaseChartDataRange span() const;

  // non-property methods:
  void addDataRange(const BaseChartDataRange& dataRange, bool simplify = true);
  void clear();
  bool isEmpty() const { return mDataRanges.isEmpty(); }
  void simplify();
  void enforceType(SelectionType type);
  bool contains(const BaseChartDataSelection& other) const;
  BaseChartDataSelection intersection(const BaseChartDataRange& other) const;
  BaseChartDataSelection intersection(
      const BaseChartDataSelection& other) const;
  BaseChartDataSelection inverse(const BaseChartDataRange& outerRange) const;

 private:
  // property members:
  QList<BaseChartDataRange> mDataRanges;

  inline static bool lessThanDataRangeBegin(const BaseChartDataRange& a,
                                            const BaseChartDataRange& b) {
    return a.begin() < b.begin();
  }
};

/*!
    Return a \ref BaseChartDataSelection with the data points in \a a joined
   with the data points in \a b. The resulting data selection is already
   simplified (see \ref BaseChartDataSelection::simplify).
*/
inline const BaseChartDataSelection operator+(const BaseChartDataSelection& a,
                                              const BaseChartDataSelection& b) {
  BaseChartDataSelection result(a);
  result += b;
  return result;
}

/*!
  Return a \ref BaseChartDataSelection with the data points in \a a joined with
  the data points in \a b. The resulting data selection is already simplified
  (see \ref BaseChartDataSelection::simplify).
*/
inline const BaseChartDataSelection operator+(const BaseChartDataRange& a,
                                              const BaseChartDataSelection& b) {
  BaseChartDataSelection result(a);
  result += b;
  return result;
}

/*!
  Return a \ref BaseChartDataSelection with the data points in \a a joined with
  the data points in \a b. The resulting data selection is already simplified
  (see \ref BaseChartDataSelection::simplify).
*/
inline const BaseChartDataSelection operator+(const BaseChartDataSelection& a,
                                              const BaseChartDataRange& b) {
  BaseChartDataSelection result(a);
  result += b;
  return result;
}

/*!
  Return a \ref BaseChartDataSelection with the data points in \a a joined with
  the data points in \a b. The resulting data selection is already simplified
  (see \ref BaseChartDataSelection::simplify).
*/
inline const BaseChartDataSelection operator+(const BaseChartDataRange& a,
                                              const BaseChartDataRange& b) {
  BaseChartDataSelection result(a);
  result += b;
  return result;
}

/*!
  Return a \ref BaseChartDataSelection with the data points which are in \a a
  but not in \a b.
*/
inline const BaseChartDataSelection operator-(const BaseChartDataSelection& a,
                                              const BaseChartDataSelection& b) {
  BaseChartDataSelection result(a);
  result -= b;
  return result;
}

/*!
  Return a \ref BaseChartDataSelection with the data points which are in \a a
  but not in \a b.
*/
inline const BaseChartDataSelection operator-(const BaseChartDataRange& a,
                                              const BaseChartDataSelection& b) {
  BaseChartDataSelection result(a);
  result -= b;
  return result;
}

/*!
  Return a \ref BaseChartDataSelection with the data points which are in \a a
  but not in \a b.
*/
inline const BaseChartDataSelection operator-(const BaseChartDataSelection& a,
                                              const BaseChartDataRange& b) {
  BaseChartDataSelection result(a);
  result -= b;
  return result;
}

/*!
  Return a \ref BaseChartDataSelection with the data points which are in \a a
  but not in \a b.
*/
inline const BaseChartDataSelection operator-(const BaseChartDataRange& a,
                                              const BaseChartDataRange& b) {
  BaseChartDataSelection result(a);
  result -= b;
  return result;
}

/*! \relates BaseChartDataRange

  Prints \a dataRange in a human readable format to the qDebug output.
*/
inline QDebug operator<<(QDebug d, const BaseChartDataRange& dataRange) {
  d.nospace() << "BaseChartDataRange(" << dataRange.begin() << ", "
              << dataRange.end() << ")";
  return d;
}

/*! \relates BaseChartDataSelection

  Prints \a selection in a human readable format to the qDebug output.
*/
inline QDebug operator<<(QDebug d, const BaseChartDataSelection& selection) {
  d.nospace() << "BaseChartDataSelection(";
  for (int i = 0; i < selection.dataRangeCount(); ++i) {
    if (i != 0) d << ", ";
    d << selection.dataRange(i);
  }
  d << ")";
  return d;
}

/* end of 'src/selection.h' */

/* including file 'src/selectionrect.h'    */
/* modified 2021-03-29T02:30:44, size 3354 */

class S_WIDGETS_EXPORT BaseChartSelectionRect : public BaseChartLayerable {
  Q_OBJECT
 public:
  explicit BaseChartSelectionRect(BaseChartCustomPlot* parentPlot);
  virtual ~BaseChartSelectionRect() Q_DECL_OVERRIDE;

  // getters:
  QRect rect() const { return mRect; }
  BaseChartRange range(const BaseChartAxis* axis) const;
  QPen pen() const { return mPen; }
  QBrush brush() const { return mBrush; }
  bool isActive() const { return mActive; }

  // setters:
  void setPen(const QPen& pen);
  void setBrush(const QBrush& brush);

  // non-property methods:
  Q_SLOT void cancel();

 signals:
  void started(QMouseEvent* event);
  void changed(const QRect& rect, QMouseEvent* event);
  void canceled(const QRect& rect, QInputEvent* event);
  void accepted(const QRect& rect, QMouseEvent* event);

 protected:
  // property members:
  QRect mRect;
  QPen mPen;
  QBrush mBrush;
  // non-property members:
  bool mActive;

  // introduced virtual methods:
  virtual void startSelection(QMouseEvent* event);
  virtual void moveSelection(QMouseEvent* event);
  virtual void endSelection(QMouseEvent* event);
  virtual void keyPressEvent(QKeyEvent* event);

  // reimplemented virtual methods
  virtual void applyDefaultAntialiasingHint(BaseChartPainter* painter) const
      Q_DECL_OVERRIDE;
  virtual void draw(BaseChartPainter* painter) Q_DECL_OVERRIDE;

  friend class BaseChartCustomPlot;
};

class S_WIDGETS_EXPORT BaseChartSelectionDecorator {
  Q_GADGET
 public:
  BaseChartSelectionDecorator();
  virtual ~BaseChartSelectionDecorator();

  // getters:
  QPen pen() const { return mPen; }
  QBrush brush() const { return mBrush; }
  BaseChartScatterStyle scatterStyle() const { return mScatterStyle; }
  BaseChartScatterStyle::ScatterProperties usedScatterProperties() const {
    return mUsedScatterProperties;
  }

  // setters:
  void setPen(const QPen& pen);
  void setBrush(const QBrush& brush);
  void setScatterStyle(const BaseChartScatterStyle& scatterStyle,
                       BaseChartScatterStyle::ScatterProperties usedProperties =
                           BaseChartScatterStyle::spPen);
  void setUsedScatterProperties(
      const BaseChartScatterStyle::ScatterProperties& properties);

  // non-virtual methods:
  void applyPen(BaseChartPainter* painter) const;
  void applyBrush(BaseChartPainter* painter) const;
  BaseChartScatterStyle getFinalScatterStyle(
      const BaseChartScatterStyle& unselectedStyle) const;

  // introduced virtual methods:
  virtual void copyFrom(const BaseChartSelectionDecorator* other);
  virtual void drawDecoration(BaseChartPainter* painter,
                              BaseChartDataSelection selection);

 protected:
  // property members:
  QPen mPen;
  QBrush mBrush;
  BaseChartScatterStyle mScatterStyle;
  BaseChartScatterStyle::ScatterProperties mUsedScatterProperties;
  // non-property members:
  BaseChartAbstractPlottable* mPlottable;

  // introduced virtual methods:
  virtual bool registerWithPlottable(BaseChartAbstractPlottable* plottable);

 private:
  Q_DISABLE_COPY(BaseChartSelectionDecorator)
  friend class BaseChartAbstractPlottable;
};

Q_DECLARE_TYPEINFO(BaseChartRange, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(BaseChartDataRange, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(BaseChartDataSelection)
Q_DECLARE_METATYPE(BaseChartSelectionDecorator*)
